<!DOCTYPE html>
<html lang="en">
<head>
  <title>Unit Testing - Born to build better enterprise frameworks and apps</title>
  <meta charset="utf-8">
  <meta name="description" content="index.description">
  <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
  <link rel="icon" href="/images/favicon.png" type="image/x-icon">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/docsearch.js/2/docsearch.min.css" />
<link rel="stylesheet" href="/css/index.css">

    <script>
    !function(t,e,a,r,c){t.TracertCmdCache=t.TracertCmdCache||[],t[c]=window[c]||
      {_isInit:!0,call:function(){t.TracertCmdCache.push(arguments)},
      start:function(t){this.call('start',t)}},t[c].l=new Date;
      var n=e.createElement(a),s=e.getElementsByTagName(a)[0];
      n.async=!0,n.src=r,s.parentNode.insertBefore(n,s)}
    (window,document,'script','https://tracert.alipay.com/tracert.js','Tracert');
      Tracert.start({
        plugins: [ 'BucName' ],
        spmAPos: 'a454',
        spmBPos: 'b4893',
      });
    </script>
  
</head>
<body>
  <div class="nav" >
  <header>
    <a href="/en/" class="nav-logo leftpadding" alt="egg"><img src="https://zos.alipayobjects.com/rmsportal/VTcUYAaoKqXyHJbLAPyF.svg"></a>
    <ul class="nav-item">
      <li>
        <form id="search-form">
          <input type="text" id="search-query" class="search-query st-default-search-input">
        </form>
      </li>
      <li><a href="/en/intro/" alt="Guide">Guide</a></li><li><a href="/api/" alt="API">API</a></li><li><a href="/en/tutorials/index.html" alt="Tutorials">Tutorials</a></li><li><a href="https://github.com/search?q=topic%3Aegg-plugin&type=Repositories" alt="Plugins">Plugins</a></li><li><a href="https://github.com/eggjs/egg/releases" alt="Release">Release</a></li>
      
      
        <li class="translations">
          <a class="nav-link">中文文档</a>
          <span class="arrow"></span><ul id="dropdownContent" class="dropdown-content"><li><a id="en" href="/en/core/unittest.html" style="color: #22ab28">English</a></li><li><a id="zh-cn" href="/zh-cn/core/unittest.html" >中文</a></li></ul>
        </li>
      
      <li><iframe src="https://ghbtns.com/github-btn.html?user=eggjs&repo=egg&type=star&count=true" frameborder="0" scrolling="0" width="150px" height="20px"></iframe></li>
    </ul>
    <a id="mobileTrigger" href="#" class="mobile-trigger">
      <ul>
        <li></li>
        <li></li>
        <li></li>
      </ul>
    </a>
  </header>
</div>
  <div id="container" class="container">
    <div class="page-main">
  <article class="markdown-body">
    <h1>Unit Testing</h1>
    <h2 id="why-unit-testing"><a class="markdown-anchor" href="#why-unit-testing">#</a> Why Unit Testing</h2>
<p>Let us start with a few questions:</p>
<ul>
<li>How to measure the quality of code</li>
<li>How to ensure the quality of code</li>
<li>Are you free to refactor code</li>
<li>How to guarantee the correctness of refactored code</li>
<li>Have you confidence to release your untested code</li>
</ul>
<p>If you are not sure, you probably need unit testing.</p>
<p>Actually, it brings us tremendous benefits:</p>
<ul>
<li>guarantee the quality of maintaining code</li>
<li>guarantee the correctness of reconstruction</li>
<li>enhance confidence</li>
<li>automation</li>
</ul>
<p>It's more important to use unit tests in a web application during the fast iteration, because each testing case can contribute to the increasing stability of the application. The result of various inputs in each test is definite, so it's obvious to detect whether the changed code has an impact on correctness or not.</p>
<p>Therefore, code, such as in Controller, Service, Helper, Extend and so on, require corresponding unit testing for quality assurances, especially modification of the framework or plugins, of which test coverage is strongly recommended to be 100%.</p>
<h2 id="test-framework"><a class="markdown-anchor" href="#test-framework">#</a> Test Framework</h2>
<p>When <a href="https://www.npmjs.com/search?q=test%20framework&amp;page=1&amp;ranking=popularity" target="_blank" rel="noopener">searching 'test framework' in npm</a>, there are a mass of test frameworks owning their own unique characteristics.</p>
<h3 id="mocha"><a class="markdown-anchor" href="#mocha">#</a> Mocha</h3>
<p>We choose and recommend you to use <a href="http://mochajs.org" target="_blank" rel="noopener">Mocha</a>, which is very rich in functionality and supports running in Node.js and Browser, what's more, it's very friendly to asynchronous test support.</p>
<blockquote>
<p>Mocha is a feature-rich JavaScript test framework running on Node.js and in the browser, making asynchronous testing simple and fun. Mocha tests run serially, allowing for flexible and accurate reporting, while mapping uncaught exceptions to the correct test cases.</p>
</blockquote>
<h3 id="ava"><a class="markdown-anchor" href="#ava">#</a> AVA</h3>
<p>Why not another recently popular framework <a href="https://github.com/avajs/ava" target="_blank" rel="noopener">AVA</a> which looks like faster? AVA is great, but practice of several projects tells us the truth that code is harder to write.</p>
<p>Comments from <a href="https://github.com/dead-horse" target="_blank" rel="noopener">@dead-horse</a>:</p>
<blockquote>
<ul>
<li>AVA is not stable enough, for example, CPU capacity is going to be overloaded when plenty of files are running concurrently. The solution of setting parameter to control concurrent could work, but 'only mode' would be not functioning any more.</li>
<li>Running cases concurrently makes great demands on implementation, because each test has to be independent, especially containing mock.</li>
<li>Considering the expensive initialization of app, it's irrational of AVA to execute each file in an independent process initializing their own app while serial framework does only one time.</li>
</ul>
</blockquote>
<p>Comments from <a href="https://github.com/fool2fish" target="_blank" rel="noopener">@fool2fish</a>：</p>
<blockquote>
<ul>
<li>It's faster to use AVA in simple application(maybe too simple to judge). But it's not recommended to use in complicate one because of its considerable flaws, such as incapability of offering accurate error stacks; meanwhile, concurrency may cause service relying on other test settings to hang up which reduces the success rate of the test. Therefore, process testing, for example, CRUD operations of database, should not use AVA.</li>
</ul>
</blockquote>
<h2 id="assertion-library"><a class="markdown-anchor" href="#assertion-library">#</a> Assertion Library</h2>
<p><a href="https://www.npmjs.com/search?q=assert&amp;page=1&amp;ranking=popularity" target="_blank" rel="noopener">Assertion libraries</a>, as flourishing as test frameworks, are emerged continuously. The one we used has changed from <a href="https://nodejs.org/api/assert.html" target="_blank" rel="noopener">assert</a> to <a href="https://github.com/shouldjs/should.js" target="_blank" rel="noopener">should</a>, and then to <a href="https://github.com/Automattic/expect.js" target="_blank" rel="noopener">expect</a>
, but we are still trying to find better one.</p>
<p>In the end, we go back to the original assertion library because of the appearance of <a href="https://github.com/power-assert-js/power-assert" target="_blank" rel="noopener">power-assert</a>, which best expresses <a href="https://github.com/atian25/blog/issues/16" target="_blank" rel="noopener">『No API is the best API』</a>.</p>
<p>To be Short, Here are it's advantages:</p>
<ul>
<li>No API is the best API. Assert is all.</li>
<li>** powerful failure message **</li>
<li>** powerful failure message **</li>
<li>** powerful failure message **</li>
</ul>
<p>You may intentionally make mistakes in order to see these failure messages.
<img src="https://cloud.githubusercontent.com/assets/227713/20919940/19e83de8-bbd9-11e6-8951-bf4a332f9b5a.png" alt=""></p>
<h2 id="test-rule"><a class="markdown-anchor" href="#test-rule">#</a> Test Rule</h2>
<p>Framework defines some fundamental rules on unit testing to keep us focus on coding rather than assistant work, such as how to execute test cases.
Egg does some basic conventions for unit testing.</p>
<h3 id="directory-structure"><a class="markdown-anchor" href="#directory-structure">#</a> Directory Structure</h3>
<p>Test code is demand to be put in <code>test</code> directory, include <code>fixtures</code> and assistant scripts.</p>
<p>Each Test file has to be named by the pattern of <code>${filename}.test.js</code>, ending with <code>.test.js</code>.</p>
<p>For example:</p>
<figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">test</span></span><br><span class="line">├── controller</span><br><span class="line">│   └── home.test.js</span><br><span class="line">├── hello.test.js</span><br><span class="line">└── service</span><br><span class="line">    └── user.test.js</span><br></pre></td></tr></table></figure>
<h3 id="test-tool"><a class="markdown-anchor" href="#test-tool">#</a> Test Tool</h3>
<p>Consistently using <a href="./development.html#unit_testing">egg-bin to launch tests</a> , which automatically loads modules like <a href="https://mochajs.org" target="_blank" rel="noopener">Mocha</a>, <a href="https://github.com/blakeembrey/co-mocha" target="_blank" rel="noopener">co-mocha</a>, <a href="https://github.com/power-assert-js/power-assert" target="_blank" rel="noopener">power-assert</a>, <a href="https://github.com/istanbuljs/nyc" target="_blank" rel="noopener">nyc</a> into test scripts, so that we can <strong>concentrate on writing tests</strong> without wasting time on the choice of various test tools or modules.</p>
<p>The only thing you need to do is setting <code>scripts.test</code> in <code>package.json</code>.</p>
<figure class="highlight json"><table><tr><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="attr">"scripts"</span>: &#123;</span><br><span class="line">    <span class="attr">"test"</span>: <span class="string">"egg-bin test"</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>Then tests would be launched by executing <code>npm test</code> command.</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">npm test</span><br><span class="line"></span><br><span class="line">&gt; unittest-example@ test /Users/mk2/git/github.com/eggjs/examples/unittest</span><br><span class="line">&gt; egg-bin test</span><br><span class="line"></span><br><span class="line">  test/hello.test.js</span><br><span class="line">    ✓ should work</span><br><span class="line"></span><br><span class="line">  1 passing (10ms)</span><br></pre></td></tr></table></figure>
<h2 id="test-preparation"><a class="markdown-anchor" href="#test-preparation">#</a> Test Preparation</h2>
<p>This chapter introduces you how to write test, and introduction of tests for the framework and plugins are located in <a href="../advanced/framework.html">framework</a> and <a href="../advanced/plugin.html">plugin</a>.</p>
<h3 id="mock"><a class="markdown-anchor" href="#mock">#</a> mock</h3>
<p>Generally, a complete application test requires initialization and cleanup, such as deleting temporary files or destroy application. Also, we have to deal with exceptional situations like network problem and exception visit of server.</p>
<p>We extracted an <a href="https://github.com/eggjs/egg-mock" target="_blank" rel="noopener">egg-mock</a>module for mock, help for quick implementation of application unit tests, supporting fast creation of ctx to test.</p>
<h3 id="app"><a class="markdown-anchor" href="#app">#</a> app</h3>
<p>Before launching, we have to create an instance of App to test code of application-level like Controller, Middleware or Service.</p>
<p>We can easily create an app instance with Mocha's <code>before</code> hook through egg-mock.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// test/controller/home.test.js</span></span><br><span class="line"><span class="keyword">const</span> assert = <span class="built_in">require</span>(<span class="string">'assert'</span>);</span><br><span class="line"><span class="keyword">const</span> mock = <span class="built_in">require</span>(<span class="string">'egg-mock'</span>);</span><br><span class="line"></span><br><span class="line">describe(<span class="string">'test/controller/home.test.js'</span>, () =&gt; &#123;</span><br><span class="line">  <span class="keyword">let</span> app;</span><br><span class="line">  before(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">// create a current app instance</span></span><br><span class="line">    app = mock.app();</span><br><span class="line">    <span class="comment">// execute tests after app is ready</span></span><br><span class="line">    <span class="keyword">return</span> app.ready();</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<p>Now, we have an app instance, and it's the base of all the following tests. See more about app at <a href="https://github.com/eggjs/egg-mock#options" target="_blank" rel="noopener"><code>mock.app(options)</code></a>.</p>
<p>It's redundancy to create an instance in each test file, so we offered an bootstrap file in egg-mock to create it conveniently.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// test/controller/home.test.js</span></span><br><span class="line"><span class="keyword">const</span> &#123; app, mock, assert &#125; = <span class="built_in">require</span>(<span class="string">'egg-mock/bootstrap'</span>);</span><br><span class="line"></span><br><span class="line">describe(<span class="string">'test/controller/home.test.js'</span>, () =&gt; &#123;</span><br><span class="line">  <span class="comment">// test cases</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<h3 id="ctx"><a class="markdown-anchor" href="#ctx">#</a> ctx</h3>
<p>Except app, tests for Extend, Service and Helper are also taken into consideration. Let's create a context through <a href="https://github.com/eggjs/egg-mock#appmockcontextoptions" target="_blank" rel="noopener"><code>app.mockContext(options)</code></a> offered by egg-mock.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">it(<span class="string">'should get a ctx'</span>, () =&gt; &#123;</span><br><span class="line">  <span class="keyword">const</span> ctx = app.mockContext();</span><br><span class="line">  assert(ctx.method === <span class="string">'GET'</span>);</span><br><span class="line">  assert(ctx.url === <span class="string">'/'</span>);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<p>If we want to mock the data for <code>ctx.user</code>, we can do that by passing the data parameter to mockContext:</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">it(<span class="string">'should mock ctx.user'</span>, () =&gt; &#123;</span><br><span class="line">  <span class="keyword">const</span> ctx = app.mockContext(&#123;</span><br><span class="line">    user: &#123;</span><br><span class="line">      name: <span class="string">'fengmk2'</span>,</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;);</span><br><span class="line">  assert(ctx.user);</span><br><span class="line">  assert(ctx.user.name === <span class="string">'fengmk2'</span>);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<p>Since we have got the app and the context, you are free to do a lot of tests.</p>
<h2 id="testing-order"><a class="markdown-anchor" href="#testing-order">#</a> Testing Order</h2>
<p>Pay close attention to testing order, and make sure any chunk of code is executed as you expected.</p>
<p>Common Error:</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// Bad</span></span><br><span class="line"><span class="keyword">const</span> &#123; app &#125; = <span class="built_in">require</span>(<span class="string">'egg-mock/bootstrap'</span>);</span><br><span class="line"></span><br><span class="line">describe(<span class="string">'bad test'</span>, () =&gt; &#123;</span><br><span class="line">  doSomethingBefore();</span><br><span class="line"></span><br><span class="line">  it(<span class="string">'should redirect'</span>, () =&gt; &#123;</span><br><span class="line">    <span class="keyword">return</span> app.httpRequest()</span><br><span class="line">      .get(<span class="string">'/'</span>)</span><br><span class="line">      .expect(<span class="number">302</span>);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<p>Mocha is going to load all the code in the beginning, which means <code>doSomethingBefore</code> would be invoked before execution. It's not expected when especially using 'only' to specify the test.</p>
<p>It's supposed to locate in a <code>before</code> hook in the suite of a particular test case.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// Good</span></span><br><span class="line"><span class="keyword">const</span> &#123; app &#125; = <span class="built_in">require</span>(<span class="string">'egg-mock/bootstrap'</span>);</span><br><span class="line"></span><br><span class="line">describe(<span class="string">'good test'</span>, () =&gt; &#123;</span><br><span class="line">  before(<span class="function"><span class="params">()</span> =&gt;</span> doSomethingBefore());</span><br><span class="line"></span><br><span class="line">  it(<span class="string">'should redirect'</span>, () =&gt; &#123;</span><br><span class="line">    <span class="keyword">return</span> app.httpRequest()</span><br><span class="line">      .get(<span class="string">'/'</span>)</span><br><span class="line">      .expect(<span class="number">302</span>);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<p>Mocha have keywords - before, after, beforeEach and afterEach - to set up preconditions and clean-up after your tests. These keywords could be multiple and execute in strict order.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">describe(<span class="string">'egg test'</span>, () =&gt; &#123;</span><br><span class="line">  before(<span class="function"><span class="params">()</span> =&gt;</span> <span class="built_in">console</span>.log(<span class="string">'order 1'</span>));</span><br><span class="line">  before(<span class="function"><span class="params">()</span> =&gt;</span> <span class="built_in">console</span>.log(<span class="string">'order 2'</span>));</span><br><span class="line">  after(<span class="function"><span class="params">()</span> =&gt;</span> <span class="built_in">console</span>.log(<span class="string">'order 6'</span>));</span><br><span class="line">  beforeEach(<span class="function"><span class="params">()</span> =&gt;</span> <span class="built_in">console</span>.log(<span class="string">'order 3'</span>));</span><br><span class="line">  afterEach(<span class="function"><span class="params">()</span> =&gt;</span> <span class="built_in">console</span>.log(<span class="string">'order 5'</span>));</span><br><span class="line">  it(<span class="string">'should worker'</span>, () =&gt; <span class="built_in">console</span>.log(<span class="string">'order 4'</span>));</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<h2 id="asynchronous-test"><a class="markdown-anchor" href="#asynchronous-test">#</a> Asynchronous Test</h2>
<p>egg-bin supports asynchronous test:</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// using Promise</span></span><br><span class="line">it(<span class="string">'should redirect'</span>, () =&gt; &#123;</span><br><span class="line">  <span class="keyword">return</span> app.httpRequest()</span><br><span class="line">    .get(<span class="string">'/'</span>)</span><br><span class="line">    .expect(<span class="number">302</span>);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// using callback</span></span><br><span class="line">it(<span class="string">'should redirect'</span>, done =&gt; &#123;</span><br><span class="line">  app.httpRequest()</span><br><span class="line">    .get(<span class="string">'/'</span>)</span><br><span class="line">    .expect(<span class="number">302</span>, done);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// using async</span></span><br><span class="line">it(<span class="string">'should redirect'</span>, <span class="keyword">async</span> () =&gt; &#123;</span><br><span class="line">  <span class="keyword">await</span> app.httpRequest()</span><br><span class="line">    .get(<span class="string">'/'</span>)</span><br><span class="line">    .expect(<span class="number">302</span>);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<p>According to specific situation, you could make different choice of these ways. Multiple asynchronous test cases could be composed to one test with async function, or divided into several independent tests.</p>
<h2 id="controller-test"><a class="markdown-anchor" href="#controller-test">#</a> Controller Test</h2>
<p>It's the tough part of all application tests, since it's closely related to router configuration. We need use <code>app.httpRequest()</code> to return a real instance <a href="https://github.com/visionmedia/supertest" target="_blank" rel="noopener">SuperTest</a>, which connects Router and Controller and could also help us to examine param verification of Router by loading boundary conditions. <code>app.httpRequest()</code> is a request instance <a href="https://github.com/visionmedia/supertest" target="_blank" rel="noopener">SuperTest</a> which is encapsulated by <a href="https://github.com/eggjs/egg-mock" target="_blank" rel="noopener">egg-mock</a>.</p>
<p>Here is an <code>app/controller/home.js</code> example.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/router.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="params">app</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">const</span> &#123; router, controller &#125; = app;</span><br><span class="line">  router.get(<span class="string">'homepage'</span>, <span class="string">'/'</span>, controller.home.index);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// app/controller/home.js</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">HomeController</span> <span class="keyword">extends</span> <span class="title">Controller</span> </span>&#123;</span><br><span class="line">  <span class="keyword">async</span> index() &#123;</span><br><span class="line">    <span class="keyword">this</span>.ctx.body = <span class="string">'hello world'</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>Then a test.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// test/controller/home.test.js</span></span><br><span class="line"><span class="keyword">const</span> &#123; app, mock, assert &#125; = <span class="built_in">require</span>(<span class="string">'egg-mock/bootstrap'</span>);</span><br><span class="line"></span><br><span class="line">describe(<span class="string">'test/controller/home.test.js'</span>, () =&gt; &#123;</span><br><span class="line">  describe(<span class="string">'GET /'</span>, () =&gt; &#123;</span><br><span class="line">    it(<span class="string">'should status 200 and get the body'</span>, () =&gt; &#123;</span><br><span class="line">      <span class="comment">// load `GET /` request</span></span><br><span class="line">      <span class="keyword">return</span> app.httpRequest()</span><br><span class="line">        .get(<span class="string">'/'</span>)</span><br><span class="line">        .expect(<span class="number">200</span>) <span class="comment">// set expectation of status to 200</span></span><br><span class="line">        .expect(<span class="string">'hello world'</span>); <span class="comment">// set expectation of body to 'hello world'</span></span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    it(<span class="string">'should send multi requests'</span>, <span class="keyword">async</span> () =&gt; &#123;</span><br><span class="line">      <span class="keyword">await</span> app.httpRequest()</span><br><span class="line">        .get(<span class="string">'/'</span>)</span><br><span class="line">        .expect(<span class="number">200</span>) v</span><br><span class="line">        .expect(<span class="string">'hello world'</span>); <span class="comment">// set expectation of body to 'hello world'</span></span><br><span class="line"></span><br><span class="line">      <span class="comment">// once more</span></span><br><span class="line">      <span class="keyword">const</span> result = <span class="keyword">await</span> app.httpRequest()</span><br><span class="line">        .get(<span class="string">'/'</span>)</span><br><span class="line">        .expect(<span class="number">200</span>)</span><br><span class="line">        .expect(<span class="string">'hello world'</span>);</span><br><span class="line"></span><br><span class="line">      <span class="comment">// verify via assert</span></span><br><span class="line">      assert(result.status === <span class="number">200</span>);</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<p><code>app.httpRequest</code> based on SuperTest supports a majority of HTTP methods such as GET, POST, PUT, and it provides rich interfaces to construct request, such as a JSON POST request.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/controller/home.js</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">HomeController</span> <span class="keyword">extends</span> <span class="title">Controller</span> </span>&#123;</span><br><span class="line">  <span class="keyword">async</span> post() &#123;</span><br><span class="line">    <span class="keyword">this</span>.ctx.body = <span class="keyword">this</span>.ctx.request.body;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// test/controller/home.test.js</span></span><br><span class="line">it(<span class="string">'should status 200 and get the request body'</span>, () =&gt; &#123;</span><br><span class="line">  <span class="comment">// mock CSRF token，explain later</span></span><br><span class="line">  app.mockCsrf();</span><br><span class="line">  <span class="keyword">return</span> app.httpRequest()</span><br><span class="line">    .post(<span class="string">'/post'</span>)</span><br><span class="line">    .type(<span class="string">'form'</span>)</span><br><span class="line">    .send(&#123;</span><br><span class="line">      foo: <span class="string">'bar'</span>,</span><br><span class="line">    &#125;)</span><br><span class="line">    .expect(<span class="number">200</span>)</span><br><span class="line">    .expect(&#123;</span><br><span class="line">      foo: <span class="string">'bar'</span>,</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<p>See details at <a href="https://github.com/visionmedia/supertest#getting-started" target="_blank" rel="noopener">SuperTest Document</a>。</p>
<h3 id="mock-csrf"><a class="markdown-anchor" href="#mock-csrf">#</a> mock CSRF</h3>
<p>The security plugin of framework would enable <a href="./security.html#csrf-prevention">CSRF prevention</a> as default. Typically, tests have to precede with a request of page in order to parse CSRF token from the response, and then use the token in later POST requests. But egg-mock provides the <code>app.mockCsrf()</code> function to skip the verification of the CSRF token of requests sent by SuperTest.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">app.mockCsrf();</span><br><span class="line"><span class="keyword">return</span> app.httpRequest()</span><br><span class="line">  .post(<span class="string">'/post'</span>)</span><br><span class="line">  .type(<span class="string">'form'</span>)</span><br><span class="line">  .send(&#123;</span><br><span class="line">    foo: <span class="string">'bar'</span>,</span><br><span class="line">  &#125;)</span><br><span class="line">  .expect(<span class="number">200</span>)</span><br><span class="line">  .expect(&#123;</span><br><span class="line">    foo: <span class="string">'bar'</span>,</span><br><span class="line">  &#125;);</span><br></pre></td></tr></table></figure>
<h2 id="service-test"><a class="markdown-anchor" href="#service-test">#</a> Service Test</h2>
<p>Service is easier to test than Controller. We need to create a ctx, and then get the instance of Service via <code>ctx.service.${serviceName}</code>, and then use the instance to test.</p>
<p>For example:</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// app/service/user.js</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">UserService</span> <span class="keyword">extends</span> <span class="title">Service</span> </span>&#123;</span><br><span class="line">  <span class="keyword">async</span> get(name) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">await</span> userDatabase.get(name);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>And a test:</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">describe(<span class="string">'get()'</span>, () =&gt; &#123;</span><br><span class="line">  <span class="comment">// using generator function because of asynchronous invoking</span></span><br><span class="line">  it(<span class="string">'should get exists user'</span>, <span class="keyword">async</span> () =&gt; &#123;</span><br><span class="line">    <span class="comment">// create ctx</span></span><br><span class="line">    <span class="keyword">const</span> ctx = app.mockContext();</span><br><span class="line">    <span class="comment">// get service.user via ctx</span></span><br><span class="line">    <span class="keyword">const</span> user = <span class="keyword">await</span> ctx.service.user.get(<span class="string">'fengmk2'</span>);</span><br><span class="line">    assert(user);</span><br><span class="line">    assert(user.name === <span class="string">'fengmk2'</span>);</span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line">  it(<span class="string">'should get null when user not exists'</span>, <span class="keyword">async</span> () =&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> ctx = app.mockContext();</span><br><span class="line">    <span class="keyword">const</span> user = <span class="keyword">await</span> ctx.service.user.get(<span class="string">'fengmk1'</span>);</span><br><span class="line">    assert(!user);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<p>Of course it's just a sample, actual code would probably be more complicated.</p>
<h2 id="extend-test"><a class="markdown-anchor" href="#extend-test">#</a> Extend Test</h2>
<p>It's extendable of Application, Request, Response and Context as well as Helper, and we are able to write specific test cases for extended functions or properties.</p>
<h3 id="application"><a class="markdown-anchor" href="#application">#</a> Application</h3>
<p>When an app instance is created by egg-mock, the extended functions and properties are already available on the instance and can be tested directly.</p>
<p>For example, we extend the application in <code>app/extend/application</code> to support cache based on <a href="https://github.com/node-modules/ylru" target="_blank" rel="noopener">ylru</a>.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> LRU = <span class="built_in">Symbol</span>(<span class="string">'Application#lru'</span>);</span><br><span class="line"><span class="keyword">const</span> LRUCache = <span class="built_in">require</span>(<span class="string">'ylru'</span>);</span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  get lru() &#123;</span><br><span class="line">    <span class="keyword">if</span> (!<span class="keyword">this</span>[LRU]) &#123;</span><br><span class="line">      <span class="keyword">this</span>[LRU] = <span class="keyword">new</span> LRUCache(<span class="number">1000</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">this</span>[LRU];</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p>A corresponding test:</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">describe(<span class="string">'get lru'</span>, () =&gt; &#123;</span><br><span class="line">  it(<span class="string">'should get a lru and it work'</span>, () =&gt; &#123;</span><br><span class="line">    <span class="comment">// set cache</span></span><br><span class="line">    app.lru.set(<span class="string">'foo'</span>, <span class="string">'bar'</span>);</span><br><span class="line">    <span class="comment">// get cache</span></span><br><span class="line">    assert(app.lru.get(<span class="string">'foo'</span>) === <span class="string">'bar'</span>);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<p>As you can see, it's easy.</p>
<h3 id="context"><a class="markdown-anchor" href="#context">#</a> Context</h3>
<p>Compared to Application, you need only one more step for Context tests, which is to create an Context instance via <code>app.mockContext</code>.</p>
<p>Such as adding a property named <code>isXHR</code> to <code>app/extend/context.js</code> to present whether or not the request was submitted via <a href="https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest/setRequestHeader" target="_blank" rel="noopener">XMLHttpRequest</a>.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  get isXHR() &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">this</span>.get(<span class="string">'X-Requested-With'</span>) === <span class="string">'XMLHttpRequest'</span>;</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p>A corresponding test:</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">describe(<span class="string">'isXHR()'</span>, () =&gt; &#123;</span><br><span class="line">  it(<span class="string">'should true'</span>, () =&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> ctx = app.mockContext(&#123;</span><br><span class="line">      headers: &#123;</span><br><span class="line">        <span class="string">'X-Requested-With'</span>: <span class="string">'XMLHttpRequest'</span>,</span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;);</span><br><span class="line">    assert(ctx.isXHR === <span class="literal">true</span>);</span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line">  it(<span class="string">'should false'</span>, () =&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> ctx = app.mockContext(&#123;</span><br><span class="line">      headers: &#123;</span><br><span class="line">        <span class="string">'X-Requested-With'</span>: <span class="string">'SuperAgent'</span>,</span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;);</span><br><span class="line">    assert(ctx.isXHR === <span class="literal">false</span>);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<h3 id="request"><a class="markdown-anchor" href="#request">#</a> Request</h3>
<p>Extended properties and function are available on <code>ctx.request</code>, so they can be tested directly.</p>
<p>For example, provide a <code>isChrome</code> property to <code>app/extend/request.js</code> to verify requests whether they are from Chrome or not.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> IS_CHROME = <span class="built_in">Symbol</span>(<span class="string">'Request#isChrome'</span>);</span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  get isChrome() &#123;</span><br><span class="line">    <span class="keyword">if</span> (!<span class="keyword">this</span>[IS_CHROME]) &#123;</span><br><span class="line">      <span class="keyword">const</span> ua = <span class="keyword">this</span>.get(<span class="string">'User-Agent'</span>).toLowerCase();</span><br><span class="line">      <span class="keyword">this</span>[IS_CHROME] = ua.includes(<span class="string">'chrome/'</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">this</span>[IS_CHROME];</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p>A corresponding test:</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">describe(<span class="string">'isChrome()'</span>, () =&gt; &#123;</span><br><span class="line">  it(<span class="string">'should true'</span>, () =&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> ctx = app.mockContext(&#123;</span><br><span class="line">      headers: &#123;</span><br><span class="line">        <span class="string">'User-Agent'</span>: <span class="string">'Chrome/56.0.2924.51'</span>,</span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;);</span><br><span class="line">    assert(ctx.request.isChrome === <span class="literal">true</span>);</span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line">  it(<span class="string">'should false'</span>, () =&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> ctx = app.mockContext(&#123;</span><br><span class="line">      headers: &#123;</span><br><span class="line">        <span class="string">'User-Agent'</span>: <span class="string">'FireFox/1'</span>,</span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;);</span><br><span class="line">    assert(ctx.request.isChrome === <span class="literal">false</span>);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<h3 id="response"><a class="markdown-anchor" href="#response">#</a> Response</h3>
<p>Identical with Request, Response test could be based on <code>ctx.response</code> directly, accessing all the extended functions and properties.</p>
<p>For example, provide an <code>isSuccess</code> property to indicate current status code equal to 200 or not.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  get isSuccess() &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">this</span>.status === <span class="number">200</span>;</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p>The corresponding test:</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">describe(<span class="string">'isSuccess()'</span>, () =&gt; &#123;</span><br><span class="line">  it(<span class="string">'should true'</span>, () =&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> ctx = app.mockContext();</span><br><span class="line">    ctx.status = <span class="number">200</span>;</span><br><span class="line">    assert(ctx.response.isSuccess === <span class="literal">true</span>);</span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line">  it(<span class="string">'should false'</span>, () =&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> ctx = app.mockContext();</span><br><span class="line">    ctx.status = <span class="number">404</span>;</span><br><span class="line">    assert(ctx.response.isSuccess === <span class="literal">false</span>);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<h3 id="helper"><a class="markdown-anchor" href="#helper">#</a> Helper</h3>
<p>Similar to Service, Helper is available on ctx, which can be tested directly.</p>
<p>Such as <code>app/extend/helper.js</code>:</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  money(val) &#123;</span><br><span class="line">    <span class="keyword">const</span> lang = <span class="keyword">this</span>.ctx.get(<span class="string">'Accept-Language'</span>);</span><br><span class="line">    <span class="keyword">if</span> (lang.includes(<span class="string">'zh-CN'</span>)) &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="string">`￥ <span class="subst">$&#123;val&#125;</span>`</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">`$ <span class="subst">$&#123;val&#125;</span>`</span>;</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
<p>A corresponding test:</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">describe(<span class="string">'money()'</span>, () =&gt; &#123;</span><br><span class="line">  it(<span class="string">'should RMB'</span>, () =&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> ctx = app.mockContext(&#123;</span><br><span class="line">      <span class="comment">// mock headers of ctx</span></span><br><span class="line">      headers: &#123;</span><br><span class="line">        <span class="string">'Accept-Language'</span>: <span class="string">'zh-CN,zh;q=0.5'</span>,</span><br><span class="line">      &#125;,</span><br><span class="line">    &#125;);</span><br><span class="line">    assert(ctx.helper.money(<span class="number">100</span>) === <span class="string">'￥ 100'</span>);</span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line">  it(<span class="string">'should US Dolar'</span>, () =&gt; &#123;</span><br><span class="line">    <span class="keyword">const</span> ctx = app.mockContext();</span><br><span class="line">    assert(ctx.helper.money(<span class="number">100</span>) === <span class="string">'$ 100'</span>);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<h2 id="mock-function"><a class="markdown-anchor" href="#mock-function">#</a> Mock Function</h2>
<p>Except functions mentioned above, like <code>app.mockContext()</code> and <code>app.mockCsrf()</code>, egg-mock provides <a href="https://github.com/eggjs/egg-mock#api" target="_blank" rel="noopener">quite a few mocking functions</a> to make writing tests easier.</p>
<ul>
<li>To prevent console logs through <code>mock.consoleLevel('NONE')</code></li>
<li>To mock session data through <code>app.mockSession(data)</code></li>
</ul>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">describe(<span class="string">'GET /session'</span>, () =&gt; &#123;</span><br><span class="line">  it(<span class="string">'should mock session work'</span>, () =&gt; &#123;</span><br><span class="line">    app.mockSession(&#123;</span><br><span class="line">      foo: <span class="string">'bar'</span>,</span><br><span class="line">      uid: <span class="number">123</span>,</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="keyword">return</span> app.httpRequest()</span><br><span class="line">      .get(<span class="string">'/session'</span>)</span><br><span class="line">      .expect(<span class="number">200</span>)</span><br><span class="line">      .expect(&#123;</span><br><span class="line">        session: &#123;</span><br><span class="line">          foo: <span class="string">'bar'</span>,</span><br><span class="line">          uid: <span class="number">123</span>,</span><br><span class="line">        &#125;,</span><br><span class="line">      &#125;);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<p>Remember to restore mock data in an <code>afterEach</code> hook, otherwise it would take effect with all the tests that supposed to be independent to each other.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">describe(<span class="string">'some test'</span>, () =&gt; &#123;</span><br><span class="line">  <span class="comment">// before hook</span></span><br><span class="line"></span><br><span class="line">  afterEach(mock.restore);</span><br><span class="line"></span><br><span class="line">  <span class="comment">// it tests</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<p><strong>When you use <code>egg-mock/bootstrap</code>, resetting work would be done automatically in an <code>afterEach</code> hook, Do not need to write these code any more.</strong></p>
<p>The following will describe the common usage of egg-mock.</p>
<h3 id="mock-properties-and-functions"><a class="markdown-anchor" href="#mock-properties-and-functions">#</a> Mock Properties And Functions</h3>
<p>Egg-mock is extended from <a href="https://github.com/node-modules/mm" target="_blank" rel="noopener">mm</a> module which contains full features of mm, so we can directly mock any objects' properties and functions.</p>
<h4 id="mock-properties"><a class="markdown-anchor" href="#mock-properties">#</a> Mock Properties</h4>
<p>Mock <code>app.config.baseDir</code> to return a given value - <code>/tmp/mockapp</code>.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">mock(app.config, <span class="string">'baseDir'</span>, <span class="string">'/tmp/mockapp'</span>);</span><br><span class="line">assert(app.config.baseDir === <span class="string">'/tmp/mockapp'</span>);</span><br></pre></td></tr></table></figure>
<h4 id="mock-functions"><a class="markdown-anchor" href="#mock-functions">#</a> Mock Functions</h4>
<p>Mock <code>fs.readFileSync</code> to return a given function.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">mock(fs, <span class="string">'readFileSync'</span>, filename =&gt; &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="string">'hello world'</span>;</span><br><span class="line">&#125;);</span><br><span class="line">assert(fs.readFileSync(<span class="string">'foo.txt'</span>) === <span class="string">'hello world'</span>);</span><br></pre></td></tr></table></figure>
<p>See more detail in <a href="https://github.com/node-modules/mm#api" target="_blank" rel="noopener">mm API</a>, include advanced usage like <code>mock.data()</code>，<code>mock.error()</code> and so on.</p>
<h3 id="mock-service"><a class="markdown-anchor" href="#mock-service">#</a> Mock Service</h3>
<p>Service is a standard built-in member of the framework, <code>app.mockService(service, methodName, fn)</code> is offered to conveniently mock its result.</p>
<p>For example, mock the method <code>get(name)</code> in <code>app/service/user</code> to return a nonexistent user.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">it(<span class="string">'should mock fengmk1 exists'</span>, () =&gt; &#123;</span><br><span class="line">  app.mockService(<span class="string">'user'</span>, <span class="string">'get'</span>, () =&gt; &#123;</span><br><span class="line">    <span class="keyword">return</span> &#123;</span><br><span class="line">      name: <span class="string">'fengmk1'</span>,</span><br><span class="line">    &#125;;</span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> app.httpRequest()</span><br><span class="line">    .get(<span class="string">'/user?name=fengmk1'</span>)</span><br><span class="line">    .expect(<span class="number">200</span>)</span><br><span class="line">    <span class="comment">// return an originally nonexistent user</span></span><br><span class="line">    .expect(&#123;</span><br><span class="line">      name: <span class="string">'fengmk1'</span>,</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<p>Using <code>app.mockServiceError(service, methodName, error)</code> to mock exception.</p>
<p>For example, mock the method <code>get(name)</code> in <code>app/service/user</code> to throw an exception.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">it(<span class="string">'should mock service error'</span>, () =&gt; &#123;</span><br><span class="line">  app.mockServiceError(<span class="string">'user'</span>, <span class="string">'get'</span>, <span class="string">'mock user service error'</span>);</span><br><span class="line">  <span class="keyword">return</span> app.httpRequest()</span><br><span class="line">    .get(<span class="string">'/user?name=fengmk2'</span>)</span><br><span class="line">    <span class="comment">// service exception causing the 500 status code</span></span><br><span class="line">    .expect(<span class="number">500</span>)</span><br><span class="line">    .expect(<span class="regexp">/mock user service error/</span>);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<h3 id="mock-httpclient"><a class="markdown-anchor" href="#mock-httpclient">#</a> Mock HttpClient</h3>
<p>External HTTP requests should be performed though <a href="./httpclient.html">HttpClient</a>, a built-in member of Egg, and <code>app.mockHttpclient(url, method, data)</code> is able to simulate various network exceptions of requests performed by <code>app.curl</code> and <code>ctx.curl</code>.</p>
<p>For example, we submit a request in <code>app/controller/home.js</code>.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">HomeController</span> <span class="keyword">extends</span> <span class="title">Controller</span> </span>&#123;</span><br><span class="line">  <span class="keyword">async</span> httpclient () &#123;</span><br><span class="line">    <span class="keyword">const</span> res = <span class="keyword">await</span> <span class="keyword">this</span>.ctx.curl(<span class="string">'https://eggjs.org'</span>);</span><br><span class="line">    <span class="keyword">this</span>.ctx.body = res.data.toString();</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>Then mock it's response.</p>
<figure class="highlight js"><table><tr><td class="code"><pre><span class="line">describe(<span class="string">'GET /httpclient'</span>, () =&gt; &#123;</span><br><span class="line">  it(<span class="string">'should mock httpclient response'</span>, () =&gt; &#123;</span><br><span class="line">    app.mockHttpclient(<span class="string">'https://eggjs.org'</span>, &#123;</span><br><span class="line">      <span class="comment">// parameter allowed to be a buffer / string / json,</span></span><br><span class="line">      <span class="comment">// will be finally converted to buffer</span></span><br><span class="line">      <span class="comment">// according to options.dataType</span></span><br><span class="line">      data: <span class="string">'mock eggjs.org response'</span>,</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="keyword">return</span> app.httpRequest()</span><br><span class="line">      .get(<span class="string">'/httpclient'</span>)</span><br><span class="line">      .expect(<span class="string">'mock eggjs.org response'</span>);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<h2 id="sample-code"><a class="markdown-anchor" href="#sample-code">#</a> Sample Code</h2>
<p>All sample code can be found in <a href="https://github.com/eggjs/examples/blob/master/unittest" target="_blank" rel="noopener">eggjs/exmaples/unittest</a></p>

  </article>
  <aside id="mobileAside" class="toc">
  <div class="mobile-menu">
    <ul>
      <li><a href="/en/intro/" alt="Guide">Guide</a></li><li><a href="/api/" alt="API">API</a></li><li><a href="/en/tutorials/index.html" alt="Tutorials">Tutorials</a></li><li><a href="https://github.com/search?q=topic%3Aegg-plugin&type=Repositories" alt="Plugins">Plugins</a></li><li><a href="https://github.com/eggjs/egg/releases" alt="Release">Release</a></li>
      
      
        <li class="translations">
          <a class="nav-link">中文文档</a>
          <span class="arrow"></span><ul id="dropdownContent" class="dropdown-content"><li><a id="en" href="/en/core/unittest.html" style="color: #22ab28">English</a></li><li><a id="zh-cn" href="/zh-cn/core/unittest.html" >中文</a></li></ul>
        </li>
      
    </ul>
  </div>
  <dl><dt id="title-Intro" style="cursor: pointer;" class="aside-title">Guide<a id="collapse-icon-Intro" class="icon opend"></a></dt><dd id=panel-Intro><ul><li><a href="/en/intro/index.html" class="menu-link">What is Egg?</a></li><li><a href="/en/intro/egg-and-koa.html" class="menu-link">Egg and Koa</a></li><li><a href="/en/intro/quickstart.html" class="menu-link">Quick Start</a></li><li><a href="/en/tutorials/progressive.html" class="menu-link">Progressive</a></li><li><a href="/en/migration.html" class="menu-link">Migration to 2.x</a></li></ul></dd><dt id="title-Basics" style="cursor: pointer;" class="aside-title">Basis Function<a id="collapse-icon-Basics" class="icon opend"></a></dt><dd id=panel-Basics><ul><li><a href="/en/basics/structure.html" class="menu-link">Directory Structure</a></li><li><a href="/en/basics/objects.html" class="menu-link">Built-in Objects</a></li><li><a href="/en/basics/env.html" class="menu-link">Environment</a></li><li><a href="/en/basics/config.html" class="menu-link">Configuration</a></li><li><a href="/en/basics/middleware.html" class="menu-link">Middleware</a></li><li><a href="/en/basics/router.html" class="menu-link">Router</a></li><li><a href="/en/basics/controller.html" class="menu-link">Controller</a></li><li><a href="/en/basics/service.html" class="menu-link">Service</a></li><li><a href="/en/basics/plugin.html" class="menu-link">Plugin</a></li><li><a href="/en/basics/schedule.html" class="menu-link">Schedule</a></li><li><a href="/en/basics/extend.html" class="menu-link">Extend</a></li><li><a href="/en/basics/app-start.html" class="menu-link">Custom Init</a></li></ul></dd><dt id="title-Core" style="cursor: pointer;" class="aside-title">Core<a id="collapse-icon-Core" class="icon opend"></a></dt><dd id=panel-Core><ul><li><a href="/en/core/development.html" class="menu-link">Development</a></li><li><a href="/en/core/unittest.html" class="menu-link">Unit Testing</a></li><li><a href="/en/core/deployment.html" class="menu-link">Deployment</a></li><li><a href="/en/core/logger.html" class="menu-link">Logger</a></li><li><a href="/en/core/httpclient.html" class="menu-link">HttpClient</a></li><li><a href="/en/core/cookie-and-session.html" class="menu-link">Cookie and Session</a></li><li><a href="/en/core/cluster-and-ipc.html" class="menu-link">Cluster and IPC</a></li><li><a href="/en/core/view.html" class="menu-link">View</a></li><li><a href="/en/core/error-handling.html" class="menu-link">Error Handling</a></li><li><a href="/en/core/security.html" class="menu-link">Security</a></li><li><a href="/en/core/i18n.html" class="menu-link">i18n</a></li></ul></dd><dt id="title-Tutorials" style="cursor: pointer;" class="aside-title">Tutorials<a id="collapse-icon-Tutorials" class="icon opend"></a></dt><dd id=panel-Tutorials><ul><li><a href="/en/tutorials/mysql.html" class="menu-link">MySQL</a></li><li><a href="/en/tutorials/restful.html" class="menu-link">RESTful API</a></li><li><a href="/en/tutorials/passport.html" class="menu-link">Passport</a></li><li><a href="/en/tutorials/socketio.html" class="menu-link">Socket.IO</a></li><li><a href="/en/tutorials/assets.html" class="menu-link">Assets</a></li><li><a href="/en/tutorials/typescript.html" class="menu-link">TypeScript</a></li></ul></dd><dt id="title-Advanced" style="cursor: pointer;" class="aside-title">Advanced<a id="collapse-icon-Advanced" class="icon opend"></a></dt><dd id=panel-Advanced><ul><li><a href="/en/advanced/loader.html" class="menu-link">Loader</a></li><li><a href="/en/advanced/plugin.html" class="menu-link">Plugin Development</a></li><li><a href="/en/advanced/framework.html" class="menu-link">Framework</a></li><li><a href="/en/advanced/cluster-client.html" class="menu-link">Cluster Enhancement</a></li><li><a href="/en/advanced/view-plugin.html" class="menu-link">View Plugin</a></li><li><a href="/en/style-guide.html" class="menu-link">Style Guide</a></li></ul></dd><dt id="title-Community" style="cursor: pointer;" class="aside-title">Community<a id="collapse-icon-Community" class="icon opend"></a></dt><dd id=panel-Community><ul><li><a href="/en/plugins/" class="menu-link">Plugin List</a></li><li><a href="/en/contributing.html" class="menu-link">Contributing</a></li><li><a href="/en/resource.html" class="menu-link">Resource</a></li><li><a href="/en/faq.html" class="menu-link">FAQ</a></li></ul></dd></dl>
</aside>
<script>
var mobileTrigger = document.getElementById('mobileTrigger');
var mobileAside = document.getElementById('mobileAside');

var expandMenu = function(title) {
  // handle icon
  const collapseIcon = document.getElementById('collapse-icon-' + title);
  if (collapseIcon) {
    collapseIcon.className = 'icon opend';
  }
  // handle panelEle
  const panelEle = document.getElementById('panel-' + title);
  if (panelEle) {
    panelEle.className = '';
  }
}

var collapseMenu = function(title) {
  // handle icon
  const collapseIcon = document.getElementById('collapse-icon-' + title);
  if (collapseIcon) {
    collapseIcon.className = 'icon closed';
  }
  // handle panelEle
  const panelEle = document.getElementById('panel-' + title);
  if (panelEle) {
    panelEle.className = 'aside-panel-hidden';
  }
}

mobileAside.onclick = function(e) {
  const targetId = e.target.id;
  if (targetId && (targetId.indexOf('title-') > -1 || targetId.indexOf('collapse-icon-') > -1)) {
    const title = targetId.replace('title-', '').replace('collapse-icon-', '');
    try { 
      // the the browser may have no localStroage or JSON.parse may throw exception.
      const menuInfo = JSON.parse(window.localStorage.getItem('menuInfo'));
        
      // current menu status
      const curClosed = menuInfo[title] ? menuInfo[title].closed : false; // default false

      // change UI
      curClosed ? expandMenu(title) : collapseMenu(title);

      // save menuInfo to localStorage
      menuInfo[title] = { closed: !curClosed } // opposite
      window.localStorage.setItem('menuInfo', JSON.stringify(menuInfo));
    } catch (e) {}
  }
};

mobileTrigger.onclick = function(e) {
  e.preventDefault();
  if (mobileAside.className.indexOf('mobile-show') === -1) {
    mobileAside.className += ' mobile-show';
  } else {
    mobileAside.className = 'toc';
  }
};

(function() {
  // save data to localStorage because the page will refresh when user change the url.
  let menuInfo;
  try { 
    // the the browser may have no localStroage or JSON.parse may throw exception.
    menuInfo = JSON.parse(window.localStorage.getItem('menuInfo'));
    if (!menuInfo) {
      menuInfo = {};
      window.localStorage.setItem('menuInfo', JSON.stringify(menuInfo));
    }
  } catch (e) {
    menuInfo = {}; // default {}
  }

  for (const title in menuInfo) {
    if (menuInfo[title] && menuInfo[title].closed) { // menu in closed status.
      collapseMenu(title);
    } else {
      expandMenu(title);
    }
  }

  // highlight menu
  const pathname = window.location.pathname;
  const selector = `a[href="${pathname}"].menu-link,a[href="${pathname}index.html"].menu-link`;
  const menuItem = mobileAside.querySelector(selector);
  if (menuItem) { menuItem.className += ' highlight'; }
})();
</script>

</div>

  </div>
</body>
<script src="https://cdn.jsdelivr.net/docsearch.js/2/docsearch.min.js"></script>
<script>
docsearch({
  apiKey: '1561de31a86f79507ea00cdb54ce647c',
  indexName: 'eggjs',
  inputSelector: '#search-query',
});
</script>
<div class="cnzz">
<script src="https://s11.cnzz.com/z_stat.php?id=1261142226&web_id=1261142226" language="JavaScript"></script>
</div>

</html>
